//
//  UBeatTimer.cpp
//  UltraStarDeluxeIOS
//
//  Created by 李 杰军 on 12-7-14.
//  Copyright (c) 2012年 DoReiMi. All rights reserved.
//

#include <iostream>
#include "UBeatTimer.h"
#include "UNote.h"

TLyricsState::TLyricsState()
{
    fTimer = new TRelativeTimer();
    Reset();
}

TLyricsState::~TLyricsState()
{
    delete fTimer; fTimer = NULL;
}

void TLyricsState::Pause()
{
    fTimer->Pause();
    fPaused = true;
}

void TLyricsState::Start(bool WaitForTrigger)
{
    fTimer->Start(WaitForTrigger);
    fPaused = false;
    fLastClock = -1;
    fAvgSyncDiff = -1;
}

void TLyricsState::Stop()
{
    fTimer->Stop();
    fPaused = false;
}

void TLyricsState::SetCurrentTime(float Time)
{
    fTimer->SetTime(Time);
    fLastClock = -1;
    fAvgSyncDiff = -1;
}

float TLyricsState::Synchronize(float LyricTime)
{
    float MasterClock = 0;
    float TimeDiff = 0;
    float result = 0;
    
    const float AVG_HISTORY_FACTOR = 0.7f;
    const float PAUSE_THRESHOLD = 0.010f;
    const float FORWARD_THRESHOLD = 0.010f;
    
    MasterClock = fSyncSource->GetClock();
    result = LyricTime;
    
    // do not sync if lyrics are paused externally or if the timestamp is old
    if (fPaused || (MasterClock == fLastClock))
        return result;
    
    // calculate average time difference (some sort of weighted mean).
    // The bigger AVG_HISTORY_FACTOR is, the smoother is the average diff.
    // This is done as some timestamps might be wrong or even lower
    // than their predecessor.
    TimeDiff = MasterClock - LyricTime;
    if (fAvgSyncDiff == -1)
        fAvgSyncDiff = TimeDiff;
    else 
        fAvgSyncDiff = TimeDiff * (1 - AVG_HISTORY_FACTOR) +
            fAvgSyncDiff * AVG_HISTORY_FACTOR;
    
    // do not go backwards in time as this could mess up the score
    if (fAvgSyncDiff > FORWARD_THRESHOLD)
    {
        printf("Sync: %f > %f\n", MasterClock, LyricTime);
        result = LyricTime + fAvgSyncDiff;
        fTimer->SetTime(result);
        fTimer->Start();
        fAvgSyncDiff = -1;
    }
    else if (fAvgSyncDiff < -PAUSE_THRESHOLD)
    {
        // wait until timer and master clock are in sync (> 10ms)
        fTimer->Pause();
        
        printf("Pause: %f < %f\n", MasterClock, LyricTime);
    }
    else if (fTimer->GetState() == rtsPaused && fAvgSyncDiff >= 0)
    {
        fTimer->Start();
        
        printf("Pause: %f < %f\n", MasterClock, LyricTime);
    }
    fLastClock = MasterClock;
    
    return result;
}

float TLyricsState::GetCurrentTime()
{
    float result = 0;
    float LyricTime = fTimer->GetTime();
    if (fSyncSource != NULL)
        result = Synchronize(LyricTime);
    else
        result = LyricTime;
    
    return LyricTime;
}

void TLyricsState::SetSyncSource(TSyncSource *SyncSource)
{
    fSyncSource = SyncSource;
}

void TLyricsState::Reset()
{
    Stop();
    fPaused = false;
    
    fSyncSource = NULL;
    
    StartTime = 0;
    TotalTime = 0;
    
    OldBeat = -1;
    MidBeat = -1;
    CurrentBeat = -1;
    
    OldBeatC = -1;
    MidBeatC = -1;
    CurrentBeatC = -1;
    
    OldBeatD = -1;
    MidBeatD = -1;
    CurrentBeatD = -1;
}

/**
 * Updates the beat information (CurrentBeat/MidBeat/...) according to the 
 * current lyric time.
 */
void TLyricsState::UpdateBeats()
{
    float CurLyricsTime = GetCurrentTime();
    
    OldBeat = CurrentBeat;
    MidBeat = GetMidBeat(CurLyricsTime - StartTime / 1000);
    CurrentBeat = floor(MidBeat);
    
    OldBeatC = CurrentBeatC;
    MidBeatC = GetMidBeat(CurLyricsTime - StartTime / 1000);
    CurrentBeatC = floor(MidBeatC);
    
    OldBeatD = CurrentBeatD;
    MidBeatD = -0.5 + GetMidBeat(CurLyricsTime - (StartTime + 120 + 20) / 1000);
    CurrentBeatD = floor(MidBeatD);
}
